package com.anji.plus.gaea.security.security.url;

import com.anji.plus.gaea.GaeaProperties;
import com.anji.plus.gaea.cache.CacheHelper;
import com.anji.plus.gaea.constant.GaeaConstant;
import com.anji.plus.gaea.holder.UserContentHolder;
import com.anji.plus.gaea.security.GaeaSecurityProperties;
import com.anji.plus.gaea.utils.GaeaUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.FilterInvocation;
import org.springframework.util.CollectionUtils;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import static com.anji.plus.gaea.constant.GaeaKeyConstant.USER_ROLE_SET_PREFIX;

/**
 * 校验Url权限
 * @author lr
 * @since 2021-02-25
 */
public class UrlAccessDecisionManager implements AccessDecisionManager {

    @Autowired
    private GaeaProperties gaeaProperties;

    @Autowired
    private CacheHelper cacheHelper;

    @Autowired
    private GaeaSecurityProperties gaeaSecurityProperties;

    /**
     * @param authentication: 当前用户登录成功后的信息
     * @param object: 请求url信息
     * @param collection: `UrlFilterInvocationSecurityMetadataSource`中的getAttributes方法传来的，表示当前请求需要的角色（可能有多个）
     * @return: void
     */
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> collection) throws AccessDeniedException, AuthenticationException {

        FilterInvocation filterInvocation = (FilterInvocation) object;
        // 获取当前请求url
        String requestUrl = filterInvocation.getRequestUrl();
        //白名单
        if (GaeaUtils.matchPath(gaeaProperties.getSecurity().getWhiteList(), requestUrl)) {
            //当符合白名单时,直接跳过
            return;
        }

        if (gaeaSecurityProperties.getAuthDisabled()) {
            return;
        }

        if (CollectionUtils.isEmpty(collection)) {
            //当前路由没有配置角色时且permitAll为true，直接放过
            if (gaeaProperties.getSecurity().isPermitAll()) {
                return;
            } else {
                throw new AccessDeniedException("Access is denied");
            }
        }
        //用户名
        String username = UserContentHolder.getContext().getUsername();
        //机构
        String orgCode = UserContentHolder.getContext().getOrgCode();
        Map<String, String> userOrgRoleMap = cacheHelper.hashGet(USER_ROLE_SET_PREFIX + username);

        if (CollectionUtils.isEmpty(userOrgRoleMap) || !userOrgRoleMap.containsKey(orgCode) || StringUtils.isBlank(userOrgRoleMap.get(orgCode))) {
            throw new AccessDeniedException("Access is denied");
        }

        //用户拥有的角色
        String[] userHasRoles = userOrgRoleMap.get(orgCode).split(GaeaConstant.SPLIT);
        // 当前用户所具有的角色
        List<String> authorities = Arrays.asList(userHasRoles);
        // 遍历当前Url需要的角色
        for (ConfigAttribute ca : collection) {
            // ① 当前url请求需要的权限
            String needRole = ca.getAttribute();
            if (authorities.contains(needRole)) {
                return;
            }
        }

        //没有权限
        throw new AccessDeniedException("Access is denied");
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}
